Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Typed FHE arguments #378

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft

Typed FHE arguments #378

wants to merge 2 commits into from

Conversation

samtay
Copy link
Contributor

@samtay samtay commented Jun 17, 2024

before

// Below, `Cipher` is not a "real" type. You never construct values of it.
#[fhe_program(scheme = "bfv")]
fn add_and_mul(a: Signed, b: Cipher<Signed>) -> (Cipher<Signed>, Cipher<Signed>) {
    (a + b, a * b)
}

let prog = add_and_mul.compile()?;
let runtime = add_and_mul.runtime()?;

let a = Signed::from(5);
// b instead has type `Ciphertext`
let b = runtime.encrypt(Signed::from(10), &public_key)?;

// runtime::run accepts a vector of inputs, so they all need to be casted to the `FheProgramInput` type.
let args: Vec<FheProgramInput> = vec![a.into(), b.into()];
let result = runtime
    .run(prog, args, &public_key)
    .unwrap();

let c: Signed = runtime.decrypt(&result[0], &private_key)?;
let d: Signed = runtime.decrypt(&result[1], &private_key)?;

after

// Now `Cipher<Signed>` is a real type - it's what you get when you encrypt a `Signed` value.
#[fhe_program(scheme = "bfv")]
fn add_and_mul(a: Signed, b: Cipher<Signed>) -> (Cipher<Signed>, Cipher<Signed>) {
    (a + b, a * b)
}

let runtime = add_and_mul.runtime()?;

let a = Signed::from(5);
// Here b has type `Cipher<Signed>`.
let b = runtime.encrypt(Signed::from(10), &public_key)?;

// Here the user passes in typed arguments and gets typed return values.
let (c_enc, d_enc) = add_and_mul.run(&runtime, &public_key, a, b)?;

let c: Signed = runtime.decrypt(&c_enc, &private_key)?;
let d: Signed = runtime.decrypt(&d_enc, &private_key)?;

outstanding work

arrays

We have one issue left, which is accepting both arrays of plaintexts and arrays of ciphertexts. The issue is that to support arbitrarily nested arrays, the impl for Into<FheProgramInput> has to be generic, but without specialization, or negative type bounds, there's no way to have two blanket impls. Perhaps there is an avenue where FHE programs instead accept e.g. Cipher<[Signed; 5]> instead of [Cipher<Signed>; 5], with appropriate Index-ing, however this might make the FHE program body awkward, as you no longer have a "first class" array.

supporting dynamic calls on generic programs

Due to limitations of stable rust, this run function needs to exist on the fhe program impl, and not a trait. We could put this on a trait, but it would likely have to go on the FheProgramExt trait (so that we don't screw up vectors of compiled FHE programs), and it would require packing all arguments into a tuple. Once the Tuple trait is stabilized we could avoid that.

Note: this fails when users have type synonyms for `Cipher` since we hackity hack check for `Cipher<T>` by string equality. Doing this properly requires the larger fix of actually using `Cipher<T>` as a bonafide typed ciphertext and not just some marker type for the fhe program signature.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant